import ar.com.coonocer.joy.mdg.templatehelpers.AssociationUtil;
import ar.com.coonocer.joy.mdg.templatehelpers.AttributeUtil;
import ar.com.coonocer.joy.mdg.templatehelpers.MultiplicityUtil;
import ar.com.coonocer.joy.mdg.templatehelpers.NamingUtil;
import ar.com.coonocer.joy.mdg.templatehelpers.ReservedWords;
import ar.com.coonocer.joy.mdg.templatehelpers.StereotypeUtil;
import ar.com.coonocer.joy.mdg.xmi.core.Association;
import ar.com.coonocer.joy.mdg.xmi.core.Attribute;
import ar.com.coonocer.joy.mdg.xmi.core.Class
import ar.com.coonocer.joy.mdg.xmi.core.Classifier;
import ar.com.coonocer.joy.mdg.xmi.core.DataType;
import ar.com.coonocer.joy.mdg.xmi.core.Operation;
import ar.com.coonocer.joy.mdg.xmi.core.Stereotype;
import ar.com.coonocer.joy.mdg.xmi.datatype.impl.MultiplicityImpl;
import ar.com.coonocer.joy.mdg.xmi.model.Package

public class XMIAttributeHelper{

	public static String getXMI(Class clazz, Attribute att){
		StringBuilder sb = new StringBuilder();
		//
		String typeType = XMIGenerator.util.getAbstractTypeType(clazz, att);
		if (!typeType.equals("Reference")){
			//
			String generatedAttName = generateAttName(att.name);
			//
			sb.append("						<UML:Attribute name=\"${generatedAttName}\" xmi.id=\"${XMIGenerator.util.getNextXmiId()}\" \r\n");
			sb.append("							visibility=\"private\" ownerScope=\"instance\" changeability=\"changeable\" \r\n");
			sb.append("							ordering=\"unordered\" targetScope=\"instance\">\r\n");
			//
			sb.append("							<UML:StructuralFeature.type>\r\n");
			sb.append("								<UML:DataType xmi.idref=\"${XMIGenerator.util.getTypeRefId(clazz, att)}\"/>\r\n");
			sb.append("							</UML:StructuralFeature.type>\r\n");
			//
			sb.append("							<UML:ModelElement.taggedValue>\r\n");
			sb.append(getXMIForTaggedValues(clazz, att, typeType));
			sb.append("							</UML:ModelElement.taggedValue>\r\n");
			//
			sb.append("						</UML:Attribute>\r\n");
		}
		//
		return sb.toString();
	}

	private static String getXMIForTaggedValues(Class clazz, Attribute att, String typeType){
		StringBuilder sb = new StringBuilder();
		//
		sb.append(getXMIForTaggedValue("attribute.column.name",att.name));
		sb.append(getXMIForTaggedValue("attribute.column.isIdentity",att.ordering.equals("ordered")));
		sb.append(getXMIForTaggedValue("attribute.column.identity.generationStrategy","*"));
		sb.append(getXMIForTaggedValue("attribute.column.isVersion","*"));
		sb.append(getXMIForTaggedValue("attribute.column.dbType",att.type?.name));
		sb.append(getXMIForTaggedValue("attribute.column.identity.generationStrategy","*"));
		sb.append(getXMIForTaggedValue("attribute.column.isVersion","*"));
		sb.append(getXMIForTaggedValue("attribute.column.notNull","*"));
		//
		sb.append(getXMIForTaggedValue("attribute.transient","*"));
		sb.append(getXMIForTaggedValue("attribute.comments","*"));
		sb.append(getXMIForTaggedValue("attribute.defaultValue","*"));
		//
		sb.append(getXMIForTaggedValue("attribute.form.visible","*"));
		sb.append(getXMIForTaggedValue("attribute.form.order","*"));
		sb.append(getXMIForTaggedValue("attribute.form.editable","*"));
		sb.append(getXMIForTaggedValue("attribute.form.style","*"));
		//
		sb.append(getXMIForTaggedValue("attribute.list.visible","*"));
		sb.append(getXMIForTaggedValue("attribute.list.order","*"));
		sb.append(getXMIForTaggedValue("attribute.list.editable","*"));
		sb.append(getXMIForTaggedValue("attribute.list.isFilter","*"));
		sb.append(getXMIForTaggedValue("attribute.list.style","*"));
		//
		//Binary
		if (typeType.equals("Binary")){}
		//
		//Boolean
		if (typeType.equals("Boolean")){}
		//
		//Date
		if (typeType.equals("Date")){
			sb.append(getXMIForTaggedValue("attribute.date.future","*"));
			sb.append(getXMIForTaggedValue("attribute.date.past","*"));
			sb.append(getXMIForTaggedValue("attribute.date.subtype","*"));
		}
		//
		//Number
		if (typeType.equals("Number")){
			sb.append(getXMIForTaggedValue("attribute.number.precision",StereotypeUtil.getTaggedValueValue(att,"precision")));
			sb.append(getXMIForTaggedValue("attribute.number.scale",StereotypeUtil.getTaggedValueValue(att,"scale")));
			sb.append(getXMIForTaggedValue("attribute.number.min","*"));
			sb.append(getXMIForTaggedValue("attribute.number.max","*"));
			sb.append(getXMIForTaggedValue("attribute.number.subtype","*"));
		}
		//
		//String
		if (typeType.equals("String")){
			sb.append(getXMIForTaggedValue("attribute.string.minSize","*"));
			sb.append(getXMIForTaggedValue("attribute.string.maxSize",StereotypeUtil.getTaggedValueValue(att,"length")));
			sb.append(getXMIForTaggedValue("attribute.string.regexp","*"));
			sb.append(getXMIForTaggedValue("attribute.string.subtype","*"));
		}
		//
		//Reference
		//No se generan aqui los atributos de tipo referencia, sino en una segunda pasada en base a las asociaciones.
		//
		return sb.toString();
	}

	public static String getXMIForTaggedValue(String taggedValueName, Object taggedValueValue){
		return XMIGenerator.getXMIForTaggedValue("Att", taggedValueName, taggedValueValue);
	}

	public static String generateAttName(String attName){
		String generatedAttName = NamingUtil.toLowerCamelCase(attName);
		if (ReservedWords.isReservedWord(generatedAttName)){
			generatedAttName += "_";
		}
		return generatedAttName;
	}

	public static String getXMIForAttributesFromAssociations(Class clazz){
		StringBuilder sb = new StringBuilder();
		//
		List<Association> associations = AssociationUtil.getAssociationsForModelElement(XMIGenerator.umlModel, clazz, AssociationUtil.BOTH);
		for (Association assoc : associations) {
			Map<String, Object> row = new HashMap<String, Object>();
			//
			Classifier end0Type = assoc.getConnection().get(0).getParticipant();
			Classifier end1Type = assoc.getConnection().get(1).getParticipant();
			//
			Integer end0Lower = MultiplicityUtil.getLower(assoc.getConnection().get(0).multiplicity);
			Integer end1Lower = MultiplicityUtil.getLower(assoc.getConnection().get(1).multiplicity);
			//
			String end0Upper = MultiplicityUtil.getUpper(assoc.getConnection().get(0).multiplicity);
			String end1Upper = MultiplicityUtil.getUpper(assoc.getConnection().get(1).multiplicity);
			//
			boolean isEnd0=end0Type.equals(clazz);
			boolean isEnd1=end1Type.equals(clazz);
			//
			String assocName=assoc.getName();
			if (assocName != null) {
				if (!assocName.startsWith("(")) {
					throw new RuntimeException("Association name must match the pattern: ( <column1> = <column2> )");
				}
				assocName = assocName.substring(1);
				//
				if (!assocName.endsWith(")")) {
					throw new RuntimeException("Association name must match the pattern: ( <column1> = <column2> )");
				}
				assocName = assocName.substring(0,assocName.length()-1);
				//
				int pos = assocName.indexOf("=");
				if (pos == -1) {
					throw new RuntimeException("Association name must match the pattern: ( <column1> = <column2> )");
				}
				//
				String joinColumnEnd0 = assocName.substring(0, pos).trim();
				String joinColumnEnd1 = assocName.substring(pos+1).trim();
				//
				if (isEnd0){
					row.put("thisEndType", end0Type);
					row.put("thisEndLower", end0Lower);
					row.put("thisEndUpper", end0Upper);
					row.put("thisEndJoinColumn", joinColumnEnd0);
					row.put("oppositeEndType", end1Type);
					row.put("oppositeEndLower", end1Lower);
					row.put("oppositeEndUpper", end1Upper);
					row.put("oppositeEndJoinColumn", joinColumnEnd1);
					sb.append(generateXMIforAttributeFromAssociation(clazz, row));
				}
				if (isEnd1){
					row.put("thisEndType", end1Type);
					row.put("thisEndLower", end1Lower);
					row.put("thisEndUpper", end1Upper);
					row.put("thisEndJoinColumn", joinColumnEnd1);
					row.put("oppositeEndType", end0Type);
					row.put("oppositeEndLower", end0Lower);
					row.put("oppositeEndUpper", end0Upper);
					row.put("oppositeEndJoinColumn", joinColumnEnd0);
					sb.append(generateXMIforAttributeFromAssociation(clazz, row));
				}
			}
		}
		//
		return sb.toString();
	}

	public static String generateXMIforAttributeFromAssociation(Class clazz, Map<String, Object> row){
		StringBuilder sb = new StringBuilder();
		//
		String thisEndJoinColumn = (String) (row.get("thisEndJoinColumn"));
		String oppositeEndJoinColumn = (String) (row.get("oppositeEndJoinColumn"));
		//
		Classifier attType = (Classifier)row.get("oppositeEndType");
		//
		String thisEndUpper = (String)row.get("thisEndUpper");
		String oppositeEndUpper = (String)row.get("oppositeEndUpper");
		//
		String cardinality = (thisEndUpper.equals("1")?"ONE":"MANY")+"_TO_"+(oppositeEndUpper.equals("1")?"ONE":"MANY");
		String generatedAttName;
		if (cardinality.endsWith("_TO_ONE")){
			generatedAttName = generateAttName(attType.getName());
		}else{
			generatedAttName = generateAttName(attType.getName()+"s");
		}
		//
		sb.append("						<UML:Attribute name=\"${generatedAttName}\" xmi.id=\"${XMIGenerator.util.getNextXmiId()}\" \r\n");
		sb.append("							visibility=\"private\" ownerScope=\"instance\" changeability=\"changeable\" \r\n");
		sb.append("							ordering=\"unordered\" targetScope=\"instance\">\r\n");
		//
		sb.append("							<UML:StructuralFeature.type>\r\n");
		sb.append("								<UML:DataType xmi.idref=\""+"Class_"+NamingUtil.toUpperCamelCase(attType.name)+"\"/>\r\n");
		sb.append("							</UML:StructuralFeature.type>\r\n");
		//
		sb.append("							<UML:ModelElement.taggedValue>\r\n");
		//
		sb.append(getXMIForTaggedValue("attribute.ref.cardinality",cardinality));
		sb.append(getXMIForTaggedValue("attribute.ref.fetch","*"));
		sb.append(getXMIForTaggedValue("attribute.ref.joinColumnName",thisEndJoinColumn));
		sb.append(getXMIForTaggedValue("attribute.ref.referencedColumnName",oppositeEndJoinColumn));
		sb.append(getXMIForTaggedValue("attribute.ref.joinTable.name","*"));
		sb.append(getXMIForTaggedValue("attribute.ref.joinTable.inv.joinColumnName","*"));
		sb.append(getXMIForTaggedValue("attribute.ref.joinTable.inv.referencedColumnName","*"));
		//
		sb.append("							</UML:ModelElement.taggedValue>\r\n");
		//
		sb.append("						</UML:Attribute>\r\n");
		//
		return sb.toString();
	}

}